/*
 Copyright (c) 2019,2020 Farooq Karimi Zadeh <fkz@riseup.net>
 VM-IRC is under GNU LGPL 3 or at your option any later version and comes
 without any warranty from author(s).
*/
const ad_txt = "VM-IRC, IRC client for KaiOS";
// ^ Advertisement text :)
const server_addr = "irc.freenode.net";
var mynick = null;
var sock = null;
var channels = null;
var firsttime = {};
var quit = false;
var hasfocus = ".left";
var users_pane_visible = true;
var lock = null;
// ^ this is for keeping screen on till VM-IRC is running.
// Thanks to perry the supreme app maker for the tip :D
var tabs;
var curr_tab;
var hunt = "";
var on_links_menu = false;

var UTF8 = {
    encode: function(s){
        return unescape(encodeURIComponent(s));
    },
    decode: function(s){
        return decodeURIComponent(escape(s));
    }
};

function create_tab(name, with_right) {
    tabs.set(name, with_right?{".left": -1, ".right": -1}:{".left": -1});
    $("main").append(`<div id=${name}></div>`);
    let t;
    if (with_right) {
        t = '<div class="left"></div><div class="right"></div>';
    } else {
        t = '<div class="left"></div>';
    }
    $("#" + $.escapeSelector(name)).html(t);
    if (!with_right) {
        $("#" + $.escapeSelector(name)+">.left").css("flex-basis", "100%");
    }
}

function moveto(dir) {
    try{
    let tabs_array = array_of_tabs();
    if (tabs_array === []) {
        return;
    }
    $("#" + $.escapeSelector(tabs_array[curr_tab])).hide();
    if (dir === "left")
        curr_tab--;
    else
        curr_tab++;

    if (curr_tab < 0)
        curr_tab += tabs_array.length;

    curr_tab %= tabs_array.length;
    $("#" + $.escapeSelector(tabs_array[curr_tab])).show();
    $("h1").text(tabs_array[curr_tab]);
    if (!is_channel(tabs_array[curr_tab]))
        hasfocus = ".left";
    move2focus_msg();
    }catch(e){
        console.log(e);
        console.log(tabs_array);
    }
}

function is_channel(name) {
    return ["#", "&"].includes(name[0]);
}

function in_tabs(name) {
    for (let tab of tabs.keys()) {
        if (tab.toUpperCase() === name.toUpperCase()) {
            return tab;
        }
    }
    return false;
}

function process_cmd(s) {
    s = s.split(" ");
    s[0] = s[0].toUpperCase();
    let commands = {
        "JOIN": args => {
            if (is_channel(args[0])) {
                sock.send("JOIN " + args[0] + "\r\n");
                create_tab(args[0], true);
            }
        },
        "PART": args => {
            if (tabs[curr_tab] === server_addr)
                return;
            sock.send("PART " + tabs[curr_tab] + " :" + ad_txt + "\r\n");
            commands["CLOSE"]("");
        },
        "CLOSE": args => {
            if (tabs[curr_tab] === server_addr)
                return;
            if (is_channel(tabs[curr_tab])) {
                return commands["PART"]("");
            }
            $("main").remove("#" + $.escapeSelector(tabs[curr_tab]));
            tabs.pop(curr_tab);
            moveto("left");
        }
    }
    commands["J"] = commands["JOIN"];
    if (commands[s[0]]) {
        commands[s[0]](s.slice(1));
    }
}

function move2focus_msg() {
    let tabs_array = Array.from(tabs.entries());
    let tb_name_es = $.escapeSelector(tabs_array[curr_tab][0]);
    let selector = `#${tb_name_es}>${hasfocus}>article`;
    let focus_n = tabs.get(tabs_array[curr_tab][0])[hasfocus];
    if (focus_n === -1) {
        let elm = $(selector).last()[0];
        if (elm) elm.focus();
    } else {
        $(selector)[focus_n].focus();
        selector = selector.replace(">article", "");
        $(selector).scrollTo(":focus");
    }
}

function redify(s) {
    return `<span style="color: red">${s}</span>`;
}

function html_escape(s) {
    // CREDIT: https://stackoverflow.com/questions/5251520/
    return s.replace(
        /\>|\<|\ /g,
        c => "&#" + c.charCodeAt(0) + ";"
    );
}

function array_of_tabs() {
    return Array.from(tabs.keys());
}

function links_menu(links) {
    on_links_menu = true;
    $("#menu").empty();
    $("main").hide();
    for (let link of links) {
        let _2add=`<article tabindex=-1><a href="${link}">${link}</a></article>`;
        $("#menu").append(_2add);
    }
    $("#menu").show();
    $("#menu > article").last()[0].focus();
}

function return_back() {
    on_links_menu = false;
    $("#menu").hide();
    $("main").show();
    move2focus_msg();
}

document.onkeydown = function (e) {
    switch (e.key) {
        case "Backspace":
            if (on_links_menu) {
                e.preventDefault();
                return_back();
            }
            break;
        case "Enter":
            if (on_links_menu) {
                var link = $("article:focus").find("a")[0].href;
                window.open(link);
                return_back();
                break;
            }
            var tab_name_es = $.escapeSelector(array_of_tabs()[curr_tab]);
            var links = $("#" + tab_name_es + ">.left>article:focus>a");
            if (links.length === 0) {
                alert("There is no link to be selected");
            } else {
                links_menu(links);
            }
            break;
        case "5":
            if (hasfocus === ".left"){
                hasfocus = ".right"; // -> users pane
            } else {
                hasfocus = ".left"; // -> messages pane
            }
            move2focus_msg();
            break;

        case "ArrowUp":
            if (on_links_menu) {
                var selector = "#menu>article";
                var curr_elm = $(selector + ":focus").index("article");
                if (curr_elm > 0) {
                    curr_elm--;
                    $(selector)[curr_elm].focus();
                }
                break;
            }
            var tabs_array = Array.from(tabs.entries());
            var curr_tab_name = tabs_array[curr_tab][0];
            var selector= "#"+$.escapeSelector(curr_tab_name)+">"+hasfocus;
            var curr_elm = tabs.get(curr_tab_name)[hasfocus];
            if (curr_elm === -1) {
                curr_elm = $(selector + ">article").length - 1;
            }
            if (curr_elm != 0) {
                curr_elm--;
                $(selector + ">article")[curr_elm].focus();
            }
            var new_values = tabs.get(curr_tab_name);
            new_values[hasfocus] = curr_elm;
            tabs.set(curr_tab_name, new_values);
            move2focus_msg();
            $(selector).scrollTo("-=37px");
            break; 

        case "ArrowDown":
            if (on_links_menu) {
                var selector = "#menu>article";
                var curr_elm = $(selector + ":focus").index("article");
                if (curr_elm <= $(selector).length) {
                    curr_elm++;
                    $(selector)[curr_elm].focus();
                }
                break;
            }

            var tabs_array = Array.from(tabs.entries());
            var curr_tab_name = tabs_array[curr_tab][0];
            var selector= "#"+$.escapeSelector(curr_tab_name)+">"+hasfocus;
            var curr_elm = tabs.get(curr_tab_name)[hasfocus];
            if (curr_elm != -1) {
                curr_elm++;
                $(selector + ">article")[curr_elm].focus();
            }
            var new_values = tabs.get(curr_tab_name);
            if (curr_elm === $(selector + ">article").length - 1) 
                curr_elm = -1;
            new_values[hasfocus] = curr_elm;
            tabs.set(curr_tab_name, new_values);
            move2focus_msg();
            $(selector).scrollTo("+=32px");
            break;

        case "ArrowLeft":
             moveto("left");
             break;
        case "ArrowRight":
            moveto("right");
            break;

        case "SoftLeft":
            if (sock == null || sock.readyState != "open"){
                alert("socket not ready!");
                break;
            }
            if (tabs[curr_tab] === server_addr) {
                alert("Please move to a channel");
                break;
            }
            var msg_text = prompt("Message");
            if (!msg_text)
                break;
            if (msg_text.startsWith("/")){
                process_cmd(msg_text.replace("/", ""));
                break;
            }
            var mynick_ = redify(mynick);
            var msg_to_add = `<article tabindex=-1 class="ownmsg">&lt;${mynick_}&gt;${msg_text}</article>`;
            var target = Array.from(tabs.keys())[curr_tab];
            selector = "#" +$.escapeSelector(target) + ">.left"; 
            $(selector).append(msg_to_add);
            $(selector + ">article").last().linky();
            msg_text = UTF8.encode(msg_text);
            sock.send("PRIVMSG "+target+ " :" + msg_text + "\r\n");
            move2focus_msg();
            break;

        case "SoftRight":
            if (sock != null && sock.readyState === "open"){
                quit = true;
                sock.send("QUIT :" + ad_txt + "\r\n");
                sock.close();
            }
            window.close();
            break;

        case "Call":
            try{
            var towhom = prompt("To whom?");
            if (towhom == null)
                break;
            var msg_text = prompt("Message to " + towhom);
            if (msg_text == null)
                break;
            
            let target_tab = in_tabs(towhom);
            if (!target_tab) {
                target_tab = towhom;
                create_tab(target_tab, false);
            }
            let selector = "#" + $.escapeSelector(target_tab) + ">.left";
            let txt2add = redify("&lt;" + mynick + "&gt;") + " " + msg_text;
            $(selector).append(`<article class="ownmsg">${txt2add}</article>`);
            while (array_of_tabs()[curr_tab] != target_tab) {
                moveto("right");
            }
            // TODO
            msg_text = UTF8.encode(msg_text);
            sock.send("PRIVMSG " + towhom + " :" + msg_text + "\r\n");
            move2focus_msg();
            }catch(e){console.log(e);}
            break;
        
        case "1":
            var current_size = Number($("body").css("fontSize").replace("px", ""));
            if (current_size === 0) break;
            current_size--;
            $("body").css("fontSize", current_size.toString() + "px");
            break;
        case "2":
            $("body").css("fontSize", "12px");
            break;
        case "3":
            var current_size = Number($("body").css("fontSize").replace("px", ""));
            if (current_size === "64") break;
            current_size++;
            $("body").css("fontSize", current_size.toString() + "px");
            break;
        
        case "*":
            var curr_tab_name = array_of_tabs()[curr_tab];
            if (is_channel(curr_tab_name)) {
                let selector = "#"+$.escapeSelector(curr_tab_name)+">.left";
                let width = $(selector).css("flex-basis");
                if (width === "75%") {
                    $(selector).css("flex-basis", "100%");
                    $(selector.replace(".left", ".right")).css("flex-basis", "0");
                    hasfocus = ".left";
                } else {
                    $(selector).css("flex-basis", "75%");
                    $(selector.replace(".left", ".right")).css("flex-basis", "25%");
                }
            }
            break;
        
        case "#":
            if (lock === null){
                lock = window.navigator.requestWakeLock("screen");
                alert("Screen will not turn off");
            } else {
                lock.unlock();
                lock = null;
                alert("Screen will turn off");
                // TODO for this message and the previous one:
                // better message
            }
            
    }
    hunt += e.key;
    if (hunt === "768286") {
        // DO NOT TELL ANYONE ABOUT THIS!
        // It's an easter egg ;)
        alert("Farooq is a POTATO!");
        hunt = "";
    }
    if (hunt.length > 7) {
        hunt = "";
    }
};

function stort() {
    tabs = new Map();
    curr_tab = 0;
    try{
    // ^ DO NOT CHANGE NAME OF THIS FUNCTION!
    var def;
    $("body").css("fontSize", "12px");
    if (mynick === null){
        def = window.localStorage.getItem("nick");
        if (["null", null].includes(def)){
            mynick = prompt("Nick (max 9 chars)");
        }else{
            mynick = prompt("Nick (max 9 chars) or press OK for " + def);
            if (mynick === ""){
                mynick = def;
            }
        }
    }
    if (mynick === null) window.close();
    window.localStorage.setItem("nick", mynick);
    if (channels === null){
        def = window.localStorage.getItem("channels");
        if (["null", null].includes(def)){
            channels = prompt("Channels(space seperated)");
        }else{
            channels = prompt("Channels(space seperated) or press OK for " + def);
            if (channels === ""){
                channels = def;
            }
        }
    }
    if (channels === null) window.close();

    window.localStorage.setItem("channels", channels);
    sock = navigator.mozTCPSocket.open(server_addr,6697,{useSecureTransport: true});

    sock.onopen = function (e) {
        e.target.send("USER vm-irc vm-irc vm-irc :VM-IRC User\r\n");
        e.target.send("NICK " + mynick + "\r\n");
        tabs.clear();
        $("main").empty();
        create_tab(server_addr, false);
        for (let channel of channels.split(" ")) {
            if (is_channel(channel)) {
                process_cmd("JOIN " + channel);
                moveto("right");
            }
        }
    };

    sock.ondata = function (e) {
        if (typeof(e.data) !== "string")
            return;
        var msgs = e.data.split("\r\n");
        for (let msg of msgs){
            if (msg.startsWith("PING")){
                e.currentTarget.send("PONG " + msg.split(" ")[1] + "\r\n");
                continue;
            }
            msg = UTF8.decode(msg);
            var token = msg.split(" ", 3);
            var theirnick = token[0].split("!")[0].replace(":", "");
            if (token[1] === "PRIVMSG"){
                try{
                var target = token[2];
                var msg_text = msg.slice(msg.search(" :") + 2, msg.length);
                msg_text = html_escape(msg_text);
                if (document.visibilityState === "hidden"){
                    if (window.Notification.permission === "granted"){
                        if (mynick === target || msg_text.includes(mynick)){
                            var vibrate_pattern = [200, 100, 200, 100, 200];
                            new window.Notification(theirnick, {body: msg_text,
                                                          vibrate:vibrate_pattern
                                                          });
                        }
                    }
                    
                }

                if (array_of_tabs().includes(target)) {
                    theirnick = "&lt;" + theirnick + "&gt;";
                } else if (target === mynick) {
                    // then it's a PM
                    let target_tab = in_tabs(theirnick);
                    if (!target_tab) {
                        target_tab = theirnick;
                        create_tab(theirnick, false);
                    }
                    let selector="#"+$.escapeSelector(target_tab)+">.left";
                    theirnick = redify(`&lt;${theirnick}&gt;`);
                    let text2add = `<article tabindex=-1>${theirnick}${msg_text}</article>`;
                    $(selector).append(text2add);
                    continue;
                }
                var text = redify(theirnick) + " " + msg_text + "<br>";
                if (msg_text.search(mynick) !== -1 || target === mynick)
                    text = "<b>" + text + "<b>";
                text = "<article tabindex=-1>" + text + "</article>";
                let selector = "#" + $.escapeSelector(target) + ">.left";
                $(selector).append(text);
                $(selector + ">article").last().linky();
                }catch(e){console.log(e);}
            }
            if (token[1] === "NOTICE"){
                var msg_text = msg.slice(msg.search(" :") + 2, msg.length);
                msg_text = html_escape(msg_text);
                var text=`<article tabindex=-1>-${theirnick}- ${msg_text}</article>`;
                let selector = "#" + $.escapeSelector(server_addr)+">.left";
                $(selector).append(text);
                $(selector + ">article").last().linky();
            }
            let selector;

            if (token[1] === "JOIN"){
                if (theirnick === mynick) continue;
                selector = "#" + $.escapeSelector(token[2]) + " > .left";
                $(selector).append(`<article tabindex=-1>${theirnick} joined.</article>`);
                selector = "#" + $.escapeSelector(token[2]) + " > .right";
                $(selector).append(`<article tabindex=-1 name="${theirnick}">${theirnick}</article>`);
            }

            if (token[1] === "PART"){
                selector = "#" + $.escapeSelector(token[2]) + " > .left";
                $(selector).append(`<article tabindex=-1>${theirnick} left.</article>`);
                selector = selector.replace(".left", ".right");
                selector += ">article[name=" + theirnick + "]";
                $(selector).remove();
            }

            if (token[1] === "QUIT"){
                for (let key of tabs.keys()) {
                    if (is_channel(key)) {
                        let key_es = $.escapeSelector(key);
                        let selector ="#"+key_es+">.right";
                        let elm =$(selector+">article[name="+theirnick+"]");
                        if (elm.length != 0) {
                            elm.remove();
                            $(selector.replace(".right", ".left")).append(`<article tabindex=-1>${theirnick} has quit.</article>`);
                        }
                    }
                }
            }
            if (token[1] === "353"){ // Channel user list
                msg = msg.replace(/:/g, "").split(" ");
                let theirnicks = msg.slice(5).join(" ");
                let channel = msg[4];
                let selector_user = "#" + $.escapeSelector(channel) + ">.right";

                // now the user list pane
                for (let anick of theirnicks.split(" ")){
                    let thing2add = `<article tabindex=-1 name="${anick}">${anick}</article>`;
                    $(selector_user).append(thing2add);
                }
            }
            if (token[1] === "366"){
                firsttime[msg.split(" ")[4]] = true; 
            }
            if (token[1] === "332"){ // Reply to TOPIC command
                let topic = msg.slice(msg.replace(":", ".").search(":") + 1, msg.length);
                topic = html_escape(topic);
                let channel = msg.split(" ")[3];
                selector = "#" + $.escapeSelector(channel) + ">.left";
                $(selector).append(`<article tabindex=-1>Topic: ${topic}</article>`);
                $(selector + ">article").last().linky();
            }
            if (token[1] === "433"){ // Nick name already in use
                mynick += "_";
                e.currentTarget.send(`NICK ${mynick}\r\n`);
            }
            if (token[1] === "NICK"){
                var newnick = token[2].replace(":", "");
                if (theirnick === mynick) {
                    mynick = newnick;
                } else {
                    let txt = `<article tabindex=-1>${theirnick} changed their nick to ${newnick}`;
                    var curr_tab_name_es = $.escapeSelector(array_of_tabs()[curr_tab]);
                    $("#" + curr_tab_name_es + ">.left").add(txt);
                    $("#" + curr_tab_name_es + ">.right").remove(`article[name=${theirnick}]`);
                    txt = `<article tabindex=-1>${newnick}</article>`;
                    $("#" + curr_tab_name_es + ">.right").add(txt);
                }
            }
            move2focus_msg();    
        }
    };
    sock.onclose = function (e) {
        if (!quit){
            stort();
        }
    };
    }catch(E){console.log(E);}
}
$(stort);
